﻿using Microsoft.Xna.Framework.Graphics;

namespace RainEngine.Rendering
{
    /// <summary>
    /// 保存几何数据。
    /// Mesh本质上就是包含额外信息的顶点缓冲。
    /// </summary>
    public class Mesh
    {
        // 顶点缓冲
        VertexBuffer        vb;
        // 索引缓冲
        IndexBuffer         ib;        
        // 绘制模式，分Indexed和Primitive两种
        DrawMode            mode;
        // 图元类型
        PrimitiveType       primitiveType;
        // 顶点数量        
        internal int verticesCount;
        // 图元数量        
        int primitiveCount;

        // 顶点声明
        VertexDeclaration vd;

        #region 构造函数
        
        /// <summary>
        /// 不使用索引创建一个Mesh对象，自动计算顶点数量和图元数量 
        /// </summary>
        /// <param name="primitiveType"></param>
        /// <param name="vb"></param>   
        public Mesh(PrimitiveType primitiveType, VertexBuffer vb, VertexDeclaration vd)
        {            
            this.primitiveType = primitiveType;            
            this.primitiveCount = CalcPrimitivesCount();
            this.vb = vb;
            this.vd = vd;
            this.verticesCount = vb.VertexCount ;            
            this.mode = DrawMode.Primitive;
        }

        /// <summary>
        /// 创建一个不使用索引的新Mesh，手动指定顶点数量和图元数量
        /// </summary>
        /// <param name="primitiveType">图元类型</param>
        /// <param name="vb">顶点缓冲</param>
        /// <param name="verticesCount">顶点数量</param>
        /// <param name="primitiveCount">图元数量</param>
        public Mesh(PrimitiveType primitiveType, VertexBuffer vb, int verticesCount, int primitiveCount)
        {
            this.primitiveType = primitiveType;
            this.vb = vb;
            this.verticesCount = verticesCount;
            this.primitiveCount = primitiveCount;
            this.mode = DrawMode.Primitive;
        }

        /// <summary>
        /// 使用索引创建一个Mesh对象，自动计算顶点数量和图元数量
        /// </summary>
        /// <param name="primitiveType">图元类型</param>
        /// <param name="vb">顶点缓冲</param>
        /// <param name="vd">顶点声明</param>
        /// <param name="ib">索引缓冲</param>
        public Mesh(PrimitiveType primitiveType, VertexBuffer vb, IndexBuffer ib)
        {
            this.primitiveType = primitiveType;
            this.primitiveCount = CalcPrimitivesCount();
            this.vb = vb;
            this.verticesCount = vb.VertexCount;
            this.ib = ib;            
            this.mode = DrawMode.Indexed;
        }   

        /// <summary>
        /// 创建一个使用索引的新Mesh，手动指定顶点数量和图元数量
        /// </summary>
        /// <param name="primitiveType">图元类型</param>
        /// <param name="vb">顶点缓冲</param>
        /// <param name="vd">顶点声明</param>
        /// <param name="ib">索引缓冲</param>
        /// <param name="verticesCount">顶点数量</param>
        /// <param name="primitiveCount">图元数量</param>
        public Mesh(PrimitiveType primitiveType, VertexBuffer vb, IndexBuffer ib, int verticesCount, int primitiveCount)
        {
            this.primitiveType = primitiveType;
            this.primitiveCount = primitiveCount;
            this.vb = vb;
            this.verticesCount = verticesCount;
            this.ib = ib;
            this.mode = DrawMode.Indexed;
        }        
        
        #endregion

        #region 属性
        
        /// <summary>
        /// 获取存储在mesh中的顶点数量。
        /// </summary>
        public int VerticesCount { get { return verticesCount; } }

        /// <summary>
        /// 获取图元数量。
        /// </summary>
        public int PrimitivesCount { get { return primitiveCount; } }

        /// <summary>
        /// 获取或设置DrawMode
        /// </summary>
        public DrawMode DrawMode { get { return mode; } }

        /// <summary>
        /// 顶点缓冲的图元类型
        /// </summary>
        public PrimitiveType PrimitiveType { get { return primitiveType; } }

        /// <summary>
        /// 获取顶点缓冲。
        /// </summary>
        public VertexBuffer VertexBuffer { get { return vb; } }

        /// <summary>
        /// 获取索引缓冲。
        /// </summary>
        public IndexBuffer IndexBuffer { get { return ib; } }        

        #endregion

        /// <summary>
        /// 计算图元数量
        /// </summary>        
        private int CalcPrimitivesCount()
        {
            int count = verticesCount / 3;

            //如果不使用索引
            if (this.DrawMode == DrawMode.Primitive)
            {
                switch (this.PrimitiveType)
                {
                    //如果为TriangleList则图元数量为顶点数量除3
                    case PrimitiveType.TriangleList:
                        count = verticesCount / 3;
                        break;
                    //如果为TriangleStrip则图元数量为顶点数量减2
                    case PrimitiveType.TriangleStrip:
                        count = verticesCount - 2;
                        break;
                }
            }
            //如果使用索引
            else
            {
                int size = ib.IndexCount;
                //int bytesPerIndex = ib.IndexElementSize == IndexElementSize.SixteenBits ? 2 : 4;

                switch (this.PrimitiveType)
                {
                    case PrimitiveType.TriangleList:
                        //count = size / bytesPerIndex / 3;
                        count = ib.IndexCount / 3;
                        break;

                    case PrimitiveType.TriangleStrip:
                        //count = ((size - 1) / bytesPerIndex / 3) + 1;
                        break;
                    case PrimitiveType.LineList:
                        count = verticesCount - 1;
                        break;
                }
            }
            return count;
        }

        /// <summary>
        /// 设置顶点缓冲和索引缓冲。
        /// </summary>
        /// <param name="gd"></param>
        public void PrepareRender(GraphicsDevice gd)
        {            
            gd.SetVertexBuffer(vb);
            if (DrawMode == DrawMode.Indexed)
                gd.Indices = ib;
        }
        
        /// <summary>
        /// 将数据发送到图形设备并进行绘制。在调用这个函数之前设备必须已经准备好(设置数据流，设置顶点声明，索引缓冲等)。
        /// </summary>
        /// <param name="gd">图形设备</param>
        public void Draw(GraphicsDevice gd)
        {
            if (DrawMode == DrawMode.Indexed)
                gd.DrawIndexedPrimitives(primitiveType, 0, 0, verticesCount, 0, primitiveCount);
            else
            {
                //gd.SetVertexBuffer(vb);
                gd.DrawPrimitives(primitiveType, 0, primitiveCount);
            }
        }
    }
}
